//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ListResiduesDistanceLigand.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <fstream>
#include <iostream>

#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListResiduesDistanceLigand::ListResiduesDistanceLigand() {
  this->location       = new String();
  this->filenameOut    = new String();
  this->filenameError  = new String();
  this->listInFilename = new String();
  this->distance       = 0;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListResiduesDistanceLigand::ListResiduesDistanceLigand(const ListResiduesDistanceLigand& listResiduesDistanceLigand) {
  this->location       = new String(listResiduesDistanceLigand.location);
  this->filenameOut    = new String(listResiduesDistanceLigand.filenameOut);
  this->filenameError  = new String(listResiduesDistanceLigand.filenameError);
  this->listInFilename = new String(listResiduesDistanceLigand.listInFilename);
  this->distance       = listResiduesDistanceLigand.distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListResiduesDistanceLigand::ListResiduesDistanceLigand(const ListResiduesDistanceLigand* listResiduesDistanceLigand) {
  this->location       = new String(listResiduesDistanceLigand->location);
  this->filenameOut    = new String(listResiduesDistanceLigand->filenameOut);
  this->filenameError  = new String(listResiduesDistanceLigand->filenameError);
  this->listInFilename = new String(listResiduesDistanceLigand->listInFilename);
  this->distance       = listResiduesDistanceLigand->distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ListResiduesDistanceLigand::~ListResiduesDistanceLigand() {
  if (this->location)       delete this->location;
  if (this->filenameOut)    delete this->filenameOut;
  if (this->filenameError)  delete this->filenameError;
  if (this->listInFilename) delete this->listInFilename;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListResiduesDistanceLigand::Location(String* location) {
  if (location) {
    if (this->location) delete this->location;
    this->location = location;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListResiduesDistanceLigand::FilenameOut(String* filenameOut) {
  if (filenameOut) {
    if (this->filenameOut) delete this->filenameOut;
    this->filenameOut = filenameOut;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListResiduesDistanceLigand::FilenameError(String* filenameError) {
  if (filenameError) {
    if (this->filenameError) delete this->filenameError;
    this->filenameError = filenameError;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListResiduesDistanceLigand::ListInFilename(String* listInFilename) {
  if (listInFilename) {
    if (this->listInFilename) delete this->listInFilename;
    this->listInFilename = listInFilename;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ListResiduesDistanceLigand::Distance(double distance) {
  this->distance = distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListResiduesDistanceLigand::Location(void) {
  return this->location;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListResiduesDistanceLigand::FilenameOut(void) {
  return this->filenameOut;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListResiduesDistanceLigand::FilenameError(void) {
  return this->filenameError;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListResiduesDistanceLigand::ListInFilename(void) {
  return this->listInFilename;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
double ListResiduesDistanceLigand::Distance(void) {
  return this->distance;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListResiduesDistanceLigand::ConstructFileUsingList() {
    
  if (!this->location || !this->listInFilename || !this->filenameOut) return false;

  TListE <String> *listFiles = new TListE <String> ();
  char             buffer[LENGTHLINE + 1];

  fstream          listFile;
  fstream          outFile;

  char *completePath = new char[strlen(this->location->Out()) + LENGTHLINE];

  listFile.open(this->listInFilename->Out(), fstream::in);
  while (!listFile.eof()) {
    listFile.getline(buffer, LENGTHLINE);
    String* nameFile = new String(buffer);
    nameFile->Trim();
    listFiles->Add(nameFile);
  }
  listFile.close();

  outFile.open(this->filenameOut->Out(), fstream::out);

  cout << "Reading Files..." << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  listFiles->SetInitial();

  while (listFiles->GetNext()) {

    DistanceBindingSite*   bindingSite = new DistanceBindingSite();

    strcpy(completePath, this->location->Out());
    strcat(completePath, listFiles->GetCurrent()->Out());
    cout << "File: " << completePath << endl;

    String* pdbId = this->ExtractPDBCodeFromNamefile(listFiles->GetCurrent());
    String* path  = new String(completePath);

    bindingSite->IDPDB(pdbId);
    bindingSite->Distance(this->distance);
    bindingSite->Location(path);

    bindingSite->ConstructPDBBindingSite();

    outFile << bindingSite->IdentFasta()->Out() << endl;
    outFile << bindingSite->ListResidues()->Out() << endl;

    cout << bindingSite->IdentFasta()->Out() << endl;
    cout << bindingSite->ListResidues()->Out() << endl;

    if (bindingSite) delete bindingSite;

  }

  outFile.close();

  if (listFiles) delete listFiles;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
 bool ListResiduesDistanceLigand::ConstructFileLargestLigandUsingList() {

  if (!this->location || !this->listInFilename || !this->filenameOut || !this->filenameError)
    return false;

  TListE <String> *listFiles = new TListE <String> ();
  char             buffer[LENGTHLINE + 1];

  fstream          listFile;
  fstream          outFile;
  fstream          errorFile;

  char *completePath = new char[strlen(this->location->Out()) + LENGTHLINE];

  listFile.open(this->listInFilename->Out(), fstream::in);
  while (!listFile.eof()) {
    listFile.getline(buffer, LENGTHLINE);
    String* nameFile = new String(buffer);
    nameFile->Trim();
    listFiles->Add(nameFile);
  }
  listFile.close();

  outFile.open(this->filenameOut->Out(), fstream::out);
  errorFile.open(this->filenameError->Out(), fstream::out);

  cout << "Reading Files..." << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  listFiles->SetInitial();

  while (listFiles->GetNext()) {

    DistanceBindingSite*   bindingSite = new DistanceBindingSite();

    strcpy(completePath, this->location->Out());
    strcat(completePath, listFiles->GetCurrent()->Out());
    cout << "File: " << completePath << endl;

    String* pdbId = this->ExtractPDBCodeFromNamefile(listFiles->GetCurrent());
    String* path  = new String(completePath);

    bindingSite->IDPDB(pdbId);
    bindingSite->Distance(this->distance);
    bindingSite->Location(path);

    UniqueLigand* uniqueLigand = bindingSite->GetLargestLingand();

    if (uniqueLigand) {

      bindingSite->ConstructPDBBindingSite(uniqueLigand);

      outFile << bindingSite->IdentFasta()->Out() << endl;
      outFile << bindingSite->ListResidues()->Out() << endl;

      cout << bindingSite->IdentFasta()->Out() << endl;
      cout << bindingSite->ListResidues()->Out() << endl;

    }
    else {
      errorFile << listFiles->GetCurrent()->Out() << endl;
      cout << "Error File: " << listFiles->GetCurrent()->Out() << endl;
    }

    if (bindingSite) delete bindingSite;

  }

  outFile.close();
  errorFile.close();

  if (listFiles) delete listFiles;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListResiduesDistanceLigand::SplitColumn(char* buffer, TListE<String>* columns, char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ListResiduesDistanceLigand::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {

  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char ListResiduesDistanceLigand::OneLetterAACode(String* code) {

  if      (!code)                return ' ';
  if      (code->Contain("ALA")) return 'A';
  else if (code->Contain("ARG")) return 'R';
  else if (code->Contain("ASN")) return 'N';
  else if (code->Contain("ASP")) return 'D';
  else if (code->Contain("ASX")) return 'B';
  else if (code->Contain("CYS")) return 'C';
  else if (code->Contain("GLU")) return 'E';
  else if (code->Contain("GLN")) return 'Q';
  else if (code->Contain("GLX")) return 'Z';
  else if (code->Contain("GLY")) return 'G';
  else if (code->Contain("HIS")) return 'H';
  else if (code->Contain("ILE")) return 'I';
  else if (code->Contain("LEU")) return 'L';
  else if (code->Contain("LYS")) return 'K';
  else if (code->Contain("MET")) return 'M';
  else if (code->Contain("PHE")) return 'F';
  else if (code->Contain("PRO")) return 'P';
  else if (code->Contain("SER")) return 'S';
  else if (code->Contain("THR")) return 'T';
  else if (code->Contain("TRP")) return 'W';
  else if (code->Contain("TYR")) return 'Y';
  else if (code->Contain("VAL")) return 'V';
  else                           return ' ';
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ListResiduesDistanceLigand::ExtractPDBCodeFromNamefile(String* nameFile) {
  if (!nameFile) return NULL;
  String* pdbId = new String();
  for (int i = 3; i < 7; i++)
    pdbId->Add(nameFile->Out(i));
  return pdbId;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
